package tools

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
	"encoding/json"

	"github.com/gogf/gf/encoding/gbase64"
	"github.com/gogf/gf/encoding/gjson"
	"github.com/gogf/gf/frame/g"
)

type encrypter struct {
	key string
}

// tools.Encrypter.BearEncode("我是个好人")
// tools.Encrypter.BearDecrypt("eyJpdiI6Ik1EZURuS1VyRFF6Zm12VnNUSm5vMUE9PSIsIm1hYyI6ImViSEwzYjFVdXVTd2I3YnhZMG1nNDQrRU5HdXFUbVoxVlNCMXp4UGtMZXc9IiwidmFsdWUiOiJnQ0s1VVZ0YTh1NmQzcTdLY3NFdU53PT0ifQ==", false)
// 上面是调用方法，可以把
var Encrypter = encrypter{
	key: "MDeDnKUrDQzfmvVsTJno1Ob4mPTekUoSphSsaeymXBM=", //base64:
	// key: "da52ac13388dbbfe45f34315f580e31e",
	//模式的ciper 方式 AES-128-CBC
}

//补码
func (e *encrypter) PKCS7Padding(ciphertext []byte, blocksize int) []byte {
	padding := blocksize - len(ciphertext)%blocksize
	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
	return append(ciphertext, padtext...)
}

//去码
func (e *encrypter) PKCS7UnPadding(origData []byte) []byte {
	length := len(origData)
	unpadding := int(origData[length-1])
	return origData[:(length - unpadding)]
}

// 加密 把 明文字符串，加密成  25+TfjamuVHKXbGMJ9SmSw5VCAZfT+KKzINvETAYmUY= 这种字符串
func (e *encrypter) AesEncrypt(paintxt string) string {
	origData := []byte(paintxt)
	// g.Log().Debug(e.key)
	k, _ := gbase64.DecodeString(e.key)
	block, _ := aes.NewCipher(k)

	blockSize := block.BlockSize()
	origData = e.PKCS7Padding(origData, blockSize)
	blockModel := cipher.NewCBCEncrypter(block, k[:blockSize])

	cryted := make([]byte, len(origData))

	blockModel.CryptBlocks(cryted, origData)

	return base64.StdEncoding.EncodeToString(cryted)
}

func (e *encrypter) AesDecrypt(cryted string) string {
	// 转成字节数组
	crytedByte, _ := base64.StdEncoding.DecodeString(cryted)
	k, _ := gbase64.DecodeString(e.key)

	// 分组秘钥
	block, _ := aes.NewCipher(k)
	// 获取秘钥块的长度
	blockSize := block.BlockSize()
	// 加密模式
	blockMode := cipher.NewCBCDecrypter(block, k[:blockSize])
	// 创建数组
	orig := make([]byte, len(crytedByte))
	// 解密
	blockMode.CryptBlocks(orig, crytedByte)
	// 去补全码
	orig = e.PKCS7UnPadding(orig)
	return string(orig)
}

//
func (e *encrypter) BearEncode(plaintext string) string {
	k, _ := gbase64.DecodeString(e.key)

	// 分组秘钥
	block, _ := aes.NewCipher(k)
	// 获取秘钥块的长度
	blockSize := block.BlockSize()
	//向量
	iv := gbase64.EncodeToString(k[:blockSize])
	data := make(map[string]string)
	data["iv"] = iv
	data["value"] = e.AesEncrypt(plaintext)
	mac := gbase64.EncodeToString(e.Hash(plaintext, k))
	data["mac"] = mac
	g.Log().Debug(data)
	jsondata, _ := json.Marshal(data)
	return gbase64.EncodeToString(jsondata)

}
func (e *encrypter) Hash(planitext string, k []byte) []byte {

	hash := hmac.New(sha256.New, k)
	hash.Write([]byte(planitext))
	return hash.Sum(nil)
}

// 解密 用php 加密的 包含iv和 value 效验
//unserialize == true 表示要用php 的unserialize 反序列化 解密结果
func (e *encrypter) BearDecrypt(payload string, unserialize bool) interface{} {
	// g.Log().Debug(payload)
	data := e.getJsonPayload(payload)
	// data 由 字段 iv，value ，mac 三个 base64 加密的 字符串
	if data == nil {
		return nil
	}
	// g.Log().Debug(data.GetString("iv"), data.GetString("value"), data.GetString("mac"))
	iv, _ := gbase64.DecodeString(data.GetString("iv"))
	// g.Log().Debug("iv=========", string(iv))
	keystring, _ := gbase64.DecodeString(e.key)
	originData, _ := gbase64.DecodeString(data.GetString("value"))
	cipherBlock, err := aes.NewCipher(keystring) // 方式 AES-128-CBC

	if err != nil {
		// g.Log().Debug("error=========", err)
		return nil
	}
	// g.Log().Debug(originData)
	// dstOriginData := originData

	cipher.NewCBCDecrypter(cipherBlock, iv).CryptBlocks(originData, originData)

	//因为这个 orginData 是一个 php serialize 字符串 需要反转成json对象

	// g.Log().Debug("===========or", string(originData))
	if unserialize {
		return UnphpSerialize(originData)
	} else {

		return originData
	}

}

//把加密的 token 解析成 json
func (e *encrypter) getJsonPayload(payload string) *gjson.Json {

	byteString, err := gbase64.DecodeString(payload)
	if err != nil {
		return nil
	}
	// g.Log().Debug(string(byteString))

	return gjson.New(byteString)
}
